package org.jsets.fastboot.routingds.jdbctemplateplus;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.sql.DataSource;
import org.jsets.fastboot.util.ReflectUtils;
import org.jsets.fastboot.util.StringUtils;
import org.springframework.jdbc.core.ArgumentPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import cn.hutool.core.util.ReflectUtil;
import lombok.extern.slf4j.Slf4j;
/**
 * 
 * JdbcTemplate 增强
 *
 * @author wangjie (https://github.com/wj596)
 * @date 2022.02.05 21:57
 * 
 */
@Slf4j
public class JdbcTemplatePlus extends JdbcTemplate {

	private final Map<String, EntityMetadata> cache = Maps.newHashMap();
	private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

	public JdbcTemplatePlus(DataSource dataSource) {
		setDataSource(dataSource);
		afterPropertiesSet();
	}

	public int insertEntity(Object entity) {
		EntityMetadata em = this.getOrCreateMetadata(entity.getClass());
		if (em.getFieldCount() == 0) {
			throw new IllegalArgumentException("实体[" + entity.getClass().getName() + "]没有效的字段");
		}

		StringBuilder sb = new StringBuilder();
		sb.append("INSERT INTO");
		sb.append(" ");
		sb.append(em.getTableName());
		sb.append(" (");
		List<Object> vls = new ArrayList<Object>(em.getFieldCount());
		for (int i = 0; i < em.getFieldCount(); i++) {
			FieldMetadata fm = em.getFieldMetadatas().get(i);
			Object v = ReflectUtil.getFieldValue(entity, fm.getField());
			if (null == v) {
				continue;
			}
			if (vls.size() > 0) {
				sb.append(", ");
			}
			sb.append(fm.getColumnName());
			vls.add(v);
		}
		sb.append(") ");

		if (vls.isEmpty()) {
			throw new IllegalArgumentException("实体[" + entity.getClass().getName() + "]没有效的字段值");
		}

		sb.append(" VALUES(");
		for (int i = 0; i < vls.size(); i++) {
			if (i > 0) {
				sb.append(", ");
			}
			sb.append("?");
		}
		sb.append(") ");

		log.info("SQL: {}, VALUES:{}", sb.toString(), vls);
		return this.update(sb.toString(), new ArgumentPreparedStatementSetter(vls.toArray()));
	}

	public int updateEntity(Object entity) {
		EntityMetadata em = this.getOrCreateMetadata(entity.getClass());
		if (em.getPrimaryKeyCount() == 0) {
			throw new IllegalArgumentException("实体[" + entity.getClass().getName() + "]没主键字段");
		}

		StringBuilder sb = new StringBuilder();
		sb.append("UPDATE");
		sb.append(" ");
		sb.append(em.getTableName());
		sb.append(" SET ");
		List<Object> vls = new ArrayList<Object>(em.getFieldCount());
		for (int i = 0; i < em.getFieldCount(); i++) {
			FieldMetadata fm = em.getFieldMetadatas().get(i);
			if (fm.isPrimaryKey()) {
				continue;
			}
			Object v = ReflectUtil.getFieldValue(entity, fm.getField());
			if (null == v) {
				continue;
			}
			if (vls.size() > 0) {
				sb.append(", ");
			}
			sb.append(fm.getColumnName() + " = ?");
			vls.add(v);
		}

		if (vls.isEmpty()) {
			throw new IllegalArgumentException("实体[" + entity.getClass().getName() + "]没有效的字段值");
		}

		sb.append(" WHERE ");
		if (em.getPrimaryKeyCount() == 1) {
			FieldMetadata pk = em.getSinglePrimaryKey();
			Object v = ReflectUtil.getFieldValue(entity, pk.getField());
			if (null == v) {
				throw new IllegalArgumentException("实体[" + entity.getClass().getName() + "]主键值为空");
			}
			sb.append(pk.getColumnName() + " = ?");
			vls.add(v);
		} else {
			int wheres = 0;
			for (FieldMetadata fm : em.getPrimaryKeys()) {
				Object v = ReflectUtil.getFieldValue(entity, fm.getField());
				if (null == v) {
					throw new IllegalArgumentException("实体[" + entity.getClass().getName() + "]主键值为空");
				}
				if (wheres > 0) {
					sb.append(" and ");
				}
				sb.append(fm.getColumnName() + " = ?");
				vls.add(v);
				wheres = wheres + 1;
			}
		}

		log.info("SQL: {}, VALUES:{}", sb.toString(), vls);
		return this.update(sb.toString(), new ArgumentPreparedStatementSetter(vls.toArray()));
	}

	public int deleteEntity(Class<?> clazz, Object id) {
		if (null == id) {
			throw new IllegalArgumentException("主键值为空");
		}

		EntityMetadata em = this.getOrCreateMetadata(clazz);
		if (em.getPrimaryKeyCount() == 0) {
			throw new IllegalArgumentException("实体[" + clazz.getName() + "]没主键字段");
		}
		if (em.getPrimaryKeyCount() > 1) {
			throw new IllegalArgumentException("实体[" + clazz.getName() + "]为联合主键，此方法不支持");
		}

		StringBuilder sb = new StringBuilder();
		sb.append("DELETE FROM ");
		sb.append(" ");
		sb.append(em.getTableName());
		sb.append(" WHERE ");
		sb.append(em.getSinglePrimaryKey().getColumnName() + " = ?");

		log.info("SQL: {}, VALUE:{}", sb.toString(), id);
		return this.update(sb.toString(), id);
	}

	protected EntityMetadata getOrCreateMetadata(Class<?> clazz) {
		String className = clazz.getName();
		EntityMetadata metadata = null;
		this.lock.readLock().lock();
		metadata = this.cache.get(className);
		this.lock.readLock().unlock();
		if (null != metadata) {
			return metadata;
		}

		this.lock.writeLock().lock();
		try {
			metadata = this.cache.get(className);
			if (null != metadata) {
				return metadata;
			}
			metadata = new EntityMetadata();
			String tableName = this.getTableName(clazz);
			Field[] fields = ReflectUtil.getFields(clazz);
			List<FieldMetadata> fms = Lists.newArrayList();
			List<FieldMetadata> pks = Lists.newArrayList();
			for (int i = 0; i < fields.length; i++) {
				Field f = fields[i];
				if (!ReflectUtils.isStatic(f.getModifiers()) && isExist(f)) {
					FieldMetadata fm = new FieldMetadata();
					fm.setColumnName(this.getColumnName(f));
					fm.setField(f);
					if (this.isPrimaryKey(f)) {
						fm.setPrimaryKey(true);
						pks.add(fm);
					}
					fms.add(fm);
				}
			}
			metadata.setTableName(tableName);
			metadata.setFieldMetadatas(fms);
			metadata.setFieldCount(fms.size());
			metadata.setPrimaryKeyCount(pks.size());
			if (pks.size() == 1) {
				metadata.setSinglePrimaryKey(pks.get(0));
			}
			if (pks.size() > 1) {
				metadata.setPrimaryKeys(pks);
			}
			this.cache.put(className, metadata);
			return metadata;
		} finally {
			this.lock.writeLock().unlock();
		}
	}

	protected String getTableName(Class<?> clazz) {
		Annotation annotation = clazz.getAnnotation(TableName.class);
		if (annotation == null) {
			return StringUtils.camelToUnderline(clazz.getSimpleName(), true);
		}
		TableName tableName = (TableName) annotation;
		String v = tableName.value();
		return v;
	}

	protected boolean isPrimaryKey(Field field) {
		Annotation annotation = field.getAnnotation(TableId.class);
		if (annotation == null) {
			return false;
		}
		return true;
	}

	protected boolean isExist(Field field) {
		Annotation annotation = field.getAnnotation(TableField.class);
		if (annotation == null) {
			return true;
		}
		TableField tableField = (TableField) annotation;
		return tableField.exist();
	}

	protected String getColumnName(Field field) {
		Annotation annotation = field.getAnnotation(TableField.class);
		if (annotation != null) {
			TableField tableField = (TableField) annotation;
			if (StringUtils.notEmpty(tableField.value())) {
				return tableField.value();
			}
		}

		annotation = field.getAnnotation(TableId.class);
		if (annotation != null) {
			TableId tableId = (TableId) annotation;
			if (StringUtils.notEmpty(tableId.value())) {
				return tableId.value();
			}
		}

		return StringUtils.camelToUnderline(field.getName(), true);
	}
}